/**
 * The Woosta Table Football Engine
 *
 * License... etc.
 **
 * Input Events
 **
 * Author: Sean Micklethwaite
 **/

#ifndef __inputH__
#define __inputH__

#include "kernel.h"
#include "sdlmisc.h"
#include "vector.h"
#include "string.h"

namespace WtfEngine
{
	/**** Game Controller ****/

	class ControllerRegisterEvent: public KeyedEvent<ControllerRegisterEvent> {
		unsigned mControllerId;		//< Local controller ID
		String	mControllerName;	//< String identifying the controller's type - useful for storing
									//< type-specific key maps
		bool	mbIsRegistered;

	public:
		inline ControllerRegisterEvent() {};
		inline ControllerRegisterEvent( unsigned id, const String& sName, bool bIsRegistered )
			: mControllerId(id), mControllerName(sName), mbIsRegistered(bIsRegistered) {};

		inline unsigned getControllerId() const { return mControllerId; };
		inline const String& getControllerName() const { return mControllerName; };
		inline bool isRegistered() const { return mbIsRegistered; };

		GC_OBJECT(ControllerRegisterEvent, sizeof(ControllerRegisterEvent) + mControllerName.length());
	};

	class ControllerEvent: public KeyedEvent<ControllerEvent> {
	private:
		unsigned mControllerId;

	public:
		inline ControllerEvent( unsigned id ): mControllerId(id) {};

		inline unsigned getControllerId() const { return mControllerId; };

		EVENT(ControllerEvent, KeyedEvent<ControllerEvent>, (
			mControllerId - pEv->mControllerId
		));
		GC_AUTOSIZE(ControllerEvent);
	};

	class ControllerAxisEvent: public ControllerEvent {
	private:
		unsigned mAxis;
		float mAxisValue;

	public:
		inline ControllerAxisEvent( unsigned id, unsigned axis, float val = 0 )
			: ControllerEvent( id ), mAxis(axis), mAxisValue(val) {};

		inline unsigned getAxis() const { return mAxis; };
		inline float getAxisValue() const { return mAxisValue; };

		EVENT(ControllerAxisEvent, ControllerEvent, (
			mAxis - pEv->mAxis
		));
		GC_AUTOSIZE(ControllerAxisEvent);
	};


	/**** Mouse events ****/
	
	typedef enum
	{
		MOUSE_NULL		=NULL,
		MOUSE_LEFT		=SDL_BUTTON_LEFT,
		MOUSE_RIGHT		=SDL_BUTTON_RIGHT,
		MOUSE_MIDDLE	=SDL_BUTTON_MIDDLE,
		MOUSE_SCROLLUP	=SDL_BUTTON_WHEELUP,
		MOUSE_SCROLLDOWN=SDL_BUTTON_WHEELDOWN
	} tMouseButton;
	
	class MouseMoveEvent : public KeyedEvent<MouseMoveEvent>
	{
	private:
		unsigned	X, Y;
		signed		DeltaX, DeltaY;

	public:
		MouseMoveEvent()
			: X(0), Y(0), DeltaX(0), DeltaY(0) {};
		MouseMoveEvent(unsigned x, unsigned y, signed dx, signed dy)
			: X(x), Y(y), DeltaX(dx), DeltaY(dy) {};

		signed GetDeltaY() const {
			return DeltaY;
		};

		signed GetDeltaX() const {
			return DeltaX;
		};

		unsigned x() const {
			return X;
		};

		unsigned y() const {
			return Y;
		};

		GC_AUTOSIZE(MouseMoveEvent);
	};

	class MouseDownEvent : public KeyedEvent<MouseDownEvent>
	{
	private:
		unsigned X, Y;
		tMouseButton Btn;
		
	public:
		MouseDownEvent()
			: X(0), Y(0), Btn(MOUSE_NULL) {
		};
		MouseDownEvent(unsigned x, unsigned y, tMouseButton b)
			: X(x), Y(y), Btn(b) {
		};
		
		unsigned x() const {
			return X;
		};
	
		unsigned y() const {
			return Y;
		};
		
		tMouseButton getButton() const{
			return Btn;
		};

		GC_AUTOSIZE(MouseDownEvent);
	};

	class MouseUpEvent : public KeyedEvent<MouseUpEvent>
	{
	private:
		unsigned X, Y;
		tMouseButton Btn;
		
	public:
		MouseUpEvent()
		: X(0), Y(0), Btn(MOUSE_NULL) {
		};
		MouseUpEvent(unsigned x, unsigned y, tMouseButton b)
		: X(x), Y(y), Btn(b) {};
		
		unsigned x() const {
			return X;
		};

		unsigned y() const {
			return Y;
		};
		
		tMouseButton getButton() const{
			return Btn;
		};

		GC_AUTOSIZE(MouseUpEvent);
	};


	struct InputKey
	{
		int		unicode;
		int		flags;
		static const int INTERNATIONAL_MASK = 0xFF80,
			UNICODE_MASK = 0x7F, KMOD_MASK = KMOD_ALT | KMOD_SHIFT | KMOD_CTRL;

		enum{
			// Use ASCII device control to represent arrow keys
			UP = 17,
   			DOWN = 18,
	  		LEFT = 19,
	 		RIGHT = 20,
			ESC = 27
		};

		InputKey() {};
		InputKey(SDL_keysym sym){
			assert( SDL_EnableUNICODE(SDL_QUERY) == SDL_ENABLE );

			this->unicode = sym.unicode;
			this->flags = sym.mod & KMOD_MASK;
		};

		InputKey(int uniKey)
			: unicode(uniKey), flags(NULL) {
		};
		InputKey(int uniKey, int f)
			: unicode(uniKey), flags(f) {
		};

		bool operator == (const InputKey& k) const {
			return (this->unicode == k.unicode && this->flags == k.flags);
		};

		bool operator < (const InputKey& k) const {
			return (this->unicode < k.unicode ||
				((this->unicode == k.unicode) && (this->flags < k.flags)));
		};
	};

	class KeyEvent : public KeyedEvent<KeyEvent>
	{
	private:
		InputKey	mKey;

	public:
		KeyEvent()
			: mKey() {};
		KeyEvent(const InputKey& key)
			: mKey(key) {};

		const InputKey& getKey() const{
			return mKey;
		};

		GC_AUTOSIZE(KeyEvent);
	};

	class KeyDownEvent : public KeyedEvent<KeyDownEvent>
	{
	private:
		InputKey	mKey;

	public:
		KeyDownEvent(const InputKey& key)
			: mKey(key) {};

		EVENT(KeyDownEvent, KeyedEvent<KeyDownEvent>, (
			mKey == pEv->mKey ? 0 : (mKey < pEv->mKey ? -1 : 1)
		));
		GC_AUTOSIZE(KeyDownEvent);
	};

	class KeyUpEvent : public KeyedEvent<KeyUpEvent>
	{
	private:
		InputKey	mKey;

	public:
		KeyUpEvent(const InputKey& key)
			: mKey(key) {};

		EVENT(KeyUpEvent, KeyedEvent<KeyUpEvent>, (
			mKey == pEv->mKey ? 0 : (mKey < pEv->mKey ? -1 : 1)
		));
		GC_AUTOSIZE(KeyUpEvent);
	};

	class InputHandler : public Task<InputHandler>
	{
	public:
		typedef std::pair<unsigned, String> tController;
		typedef std::list<tController>		tControllerList;

	private:
		std::list<tController>	mControllers;

	public:
		InputHandler()
			: Task<InputHandler>(-1000, "Input Handler") {};

		void		CaptureAllInput(bool b);
		void		EnterGameMode();
		void		EnterGuiMode();

		iVector2D	GetMousePosition() const;
		bool		IsKeyPressed(const InputKey& key) const;

		unsigned	RegisterController( const String& sName )
		{
			static unsigned cid = 0;
			unsigned id = cid++;
			mControllers.push_back( tController( id, sName ) );
			Kernel::GetInstance().ProcessEvent( new ControllerRegisterEvent( id, sName, true ) );
			return id;
		};
		void		DeregisterController( unsigned id )
		{
			for( std::list<tController>::const_iterator i = mControllers.begin(); i != mControllers.end(); i++ ) {
				if( i->first == id ) {
					Kernel::GetInstance().ProcessEvent( new ControllerRegisterEvent( id, i->second, false ) );
					mControllers.erase(i);
					break;
				};
			};
		};
		const tControllerList& getControllers() const {
			return mControllers;
		};

	protected:
		void Run();
	};
};


#endif
